package qiniu

import (
	"context"
	"fmt"
	"strconv"
	"strings"

	"mall/common/globalkey"
	"mall/service/attachment/model"
	"mall/service/attachment/rpc/internal/svc"
	"mall/service/attachment/rpc/internal/utils/errorhandler"
	"mall/service/attachment/rpc/types/attachment"

	"github.com/qiniu/go-sdk/v7/auth/qbox"
	"github.com/qiniu/go-sdk/v7/storage"
	"github.com/sirupsen/logrus"
	"github.com/volatiletech/null/v8"
	"github.com/zeromicro/go-zero/core/logx"
)

type QiniuCallbackLogic struct {
	ctx           context.Context
	svcCtx        *svc.ServiceContext
	bucketManager *storage.BucketManager
	logx.Logger
}

func NewQiniuCallbackLogic(ctx context.Context, svcCtx *svc.ServiceContext) *QiniuCallbackLogic {
	return &QiniuCallbackLogic{
		ctx:    ctx,
		svcCtx: svcCtx,
		Logger: logx.WithContext(ctx),
	}
}

func (l *QiniuCallbackLogic) QiniuCallback(in *attachment.RecordInfo) (*attachment.BaseResp, error) {
	if in.Key != nil && len(strings.TrimSpace(*in.Key)) > 0 && in.Style != nil && len(strings.TrimSpace(*in.Style)) > 0 {
		var persistentId string
		if in.Id != nil && len(strings.TrimSpace(*in.Id)) > 0 {
			persistentId = strings.TrimSpace(*in.Id)
		}
		var bucket string
		if in.Bucket != nil && len(strings.TrimSpace(*in.Bucket)) > 0 {
			bucket = strings.TrimSpace(*in.Bucket)
		} else {
			bucket = l.svcCtx.Config.QiniuConf.Bucket
		}
		info, err := l.findInfoByKey(strings.TrimSpace(*in.Key), 0)
		if err != nil {
			return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
		}

		if info == nil {
			return &attachment.BaseResp{}, nil
		}

		mac := qbox.NewMac(l.svcCtx.Config.QiniuConf.AccessKey, l.svcCtx.Config.QiniuConf.SecretKey)

		cfg := &storage.Config{
			UseHTTPS:      l.svcCtx.Config.QiniuConf.UseHTTPS,      // 是否使用https域名进行资源管理
			UseCdnDomains: l.svcCtx.Config.QiniuConf.UseCdnDomains, // 上传是否使用CDN上传加速
		}

		// 指定空间所在的区域，如果不指定将自动探测
		// 如果没有特殊需求，默认不需要指定
		// cfg.Region=&storage.ZoneHuabei
		// switch l.svcCtx.Config.QiniuConf.Region {
		// case "z2":
		// 	cfg.Region = &storage.ZoneHuanan
		// }

		l.bucketManager = storage.NewBucketManager(mac, cfg)

		if keys := strings.Split(strings.TrimSpace(*in.Style), ","); len(keys) > 0 {
			for _, key := range keys {
				fopResultInfo, err := l.findInfoByKey(strings.TrimSpace(key), info.ID)
				if err != nil {
					return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
				}

				if fopResultInfo == nil {
					newInfo := &model.AttachmentFileInfo{}
					newInfo.AttachmentID = info.AttachmentID
					newInfo.PersistentID = persistentId
					newInfo.ParentID = info.ID
					_, err = l.createOrUpdate(bucket, key, newInfo)
					if err != nil {
						return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					}
				} else if fopResultInfo.PersistentID == persistentId {
					_, err = l.createOrUpdate(bucket, key, fopResultInfo)
					if err != nil {
						return nil, errorhandler.DefaultRepositoryError(l.Logger, err, in)
					}
				}
			}
		}
	}

	return &attachment.BaseResp{}, nil
}

func (l *QiniuCallbackLogic) findInfoByKey(key string, pid uint64) (*model.AttachmentFileInfo, error) {
	filterMap := make(map[string]map[string][]any)
	filterMap[model.AttachmentFileInfoColumns.KeyValue] = map[string][]any{
		"equalTo": {key},
	}
	filterMap[model.AttachmentFileInfoColumns.DelStatus] = map[string][]any{ //特殊情况
		"notEqualTo": {globalkey.StatusDeleted},
	}
	if pid > 0 {
		filterMap[model.AttachmentFileInfoColumns.ParentID] = map[string][]any{
			"equalTo": {pid},
		}
	}
	list, _, err := l.svcCtx.FileInfo.GetList(l.ctx, model.M{
		"pageSize": uint64(1),
		"orderBy":  model.AttachmentFileInfoColumns.ID + " DESC",
	}, -1, filterMap)
	if err != nil {
		return nil, err
	}

	if len(list) > 0 {
		info := list[0]
		return info, nil
	}

	return nil, nil
}

func (l *QiniuCallbackLogic) createOrUpdate(
	bucket string,
	key string,
	data *model.AttachmentFileInfo,
) (*model.AttachmentFileInfo, error) {
	fileInfo, err := l.bucketManager.Stat(bucket, key)
	if err != nil {
		logrus.Info(fmt.Sprintf("QiniuPutFile Stat err: %+v", err))
		logx.Errorw("QiniuPutFile Stat err", logx.Field("detail", err.Error()))
		// Qiniu Stat err: no such file or directory
		return nil, err
	}

	var newData *model.AttachmentFileInfo

	if data != nil && data.ID > 0 {
		newData = data
	} else {
		newData = &model.AttachmentFileInfo{}
	}

	newData.KeyValue = key
	newData.Bucket = bucket
	newData.Size = uint64(fileInfo.Fsize)
	newData.Hash = fileInfo.Hash
	newData.MimeType = fileInfo.MimeType
	newData.Type = uint8(fileInfo.Type)
	newData.PutTime = uint64(fileInfo.PutTime)
	newData.RestoreStatus = uint8(fileInfo.RestoreStatus)
	newData.Status = uint8(fileInfo.Status)
	newData.MD5 = fileInfo.Md5
	newData.EndUser = fileInfo.EndUser
	newData.Expiration = uint64(fileInfo.Expiration)
	newData.TransitionToIa = uint64(fileInfo.TransitionToIA)
	newData.TransitionToArchive = uint64(fileInfo.TransitionToArchive)
	newData.TransitionToDeepArchive = uint64(fileInfo.TransitionToDeepArchive)
	if len(fileInfo.Parts) > 0 {
		var parts []string
		for _, partId := range fileInfo.Parts {
			parts = append(parts, strconv.FormatInt(partId, 10))
		}
		newData.Parts = strings.Join(parts, ",")
	}
	if fileInfo.MetaData != nil {
		var items []string
		for mk, mv := range fileInfo.MetaData {
			items = append(items, mk+":"+mv)
		}
		newData.MetaValue = null.StringFrom(strings.Join(items, ";"))
	}

	if newData.ID > 0 {
		err := l.svcCtx.FileInfo.Update(l.ctx, nil, newData)
		if err != nil {
			return nil, err
		}

		return newData, nil
	}

	if data != nil {
		if data.AttachmentID > 0 {
			newData.AttachmentID = data.AttachmentID
		}
		if len(data.PersistentID) > 0 {
			newData.PersistentID = data.PersistentID
		}
		if data.ParentID > 0 {
			newData.ParentID = data.ParentID
		}
	}

	err = l.svcCtx.FileInfo.Insert(l.ctx, nil, newData)
	if err != nil {
		return nil, err
	}

	return newData, err
}
